% derived from example in the documentation of SRFI27
% and translated to Erlang
-module(gauss).
-export([dist_var_new/2, init/1, dist/0]).
-record( dist_state, {state, mu, sigma}).

dist_var_new(Mu, Sigma) ->
  dist_var_new(false, Mu, Sigma).

% create the thunk
dist_var_new(State, Mu, Sigma) ->
  This = #dist_state {
    state = State,
    mu = 1.0 * Mu,
    sigma = 1.0 * Sigma},
  IntPid = spawn(gauss, init, [This]),
  register(gauss, IntPid),
  fun () ->
    IntPid ! {self(), value},
    receive
      {retval, Any} -> Any
    end
  end.

dist() ->
  fun () ->
    gauss ! {self(), value},
    receive
      {retval, Any} -> Any
    end
  end.

init(This) ->
  random:seed(now()),
  dispatch(This).

dispatch(This) ->
  receive
    {Pid, value} ->
      {NewThis, Value} = value(This),
      Pid!{retval, Value},
      dispatch(NewThis);
    stop ->
      ok
  end.

value(This) ->
  case This#dist_state.state of
    true -> Val = This#dist_state.mu
        + (This#dist_state.sigma * This#dist_state.state),
      {This#dist_state{state = false}, Val};
    _    -> sigma_loop(This)
  end.

sigma_loop(This) ->
  V1 = 2.0 * random:uniform() - 1.0,
  V2 = 2.0 * random:uniform() - 1.0,
  S  = (V1 * V1) + (V2 * V2),
  if
    S >= 1.0 ->
      sigma_loop(This);
    true     ->
      Scale = math:sqrt( (-2.0 * math:log(S)) / S),
      Val = This#dist_state.mu
        + (This#dist_state.sigma * Scale * V1),
      {This#dist_state{state = Scale * V2}, Val}
  end.

